#include "nemu.h"
#include <stdlib.h>
#include <time.h>
#include "burst.h"
void init_cache() {
    int i,j;
    for(i=0;i<cache_L1_S;i++){
        for(j=0;j<cache_L1_E;j++){
            cache_L1[i][j].valid=false;
        }
    }
    for(i=0;i<cache_L2_S;i++){
        for(j=0;j<cache_L2_E;j++){
            cache_L2[i][j].valid=false;
            cache_L2[i][j].dirty=false;
        }
    }
    srand(time(0));//随机数种子
    return;
}

void ddr3_read_me(hwaddr_t addr,void *data);//声明dram.c中ddr3_read_me函数
int read_cache_L1(hwaddr_t addr);
int read_cache_L2(hwaddr_t addr);
int read_cache_L1(hwaddr_t addr){
    uint32_t set1=(addr>>cache_L1_b)&(cache_L1_S-1);
    uint32_t tag=addr>>(cache_L1_b+cache_L1_s);
    int i1;
    for(i1=0;i1<cache_L1_E;i1++){
        if(cache_L1[set1][i1].valid&&(cache_L1[set1][i1].tag==tag)){
            return i1;
        }
    }
    int i2=read_cache_L2(addr);
    for(i1=0;i1<cache_L1_E;i1++){
        if(!cache_L1[set1][i1].valid)break;
    }
    if(i1==cache_L1_E){
        i1=rand()%cache_L1_E; 
    }
    cache_L1[set1][i1].valid=true;
    cache_L1[set1][i1].tag=tag;
    int set2=(addr>>cache_L2_b)&(cache_L2_S-1);
    memcpy(cache_L1[set1][i1].data,cache_L2[set2][i2].data,cache_L1_B);
    //ddr3_read_me(addr,cache_L1[set1][i1].data);
    return i1;
}
void ddr3_write_me(hwaddr_t addr, void* data, uint8_t* mask);
int read_cache_L2(hwaddr_t addr){
    uint32_t set=(addr>>cache_L2_b)&(cache_L2_S-1);
    uint32_t tag=addr>>(cache_L2_b+cache_L2_s);
    uint32_t addr_start=((addr>>cache_L2_b)<<cache_L2_b);
    int i;
    for(i=0;i<cache_L2_E;i++){
        if(cache_L2[set][i].valid&&(cache_L2[set][i].tag==tag)){
            return i;
        }
    }
    for(i=0;i<cache_L2_E;i++){
        if(!cache_L2[set][i].valid)break;
    }
    int i0;
    if(i==cache_L2_E){
        i=rand()%cache_L2_E; 
        if(cache_L2[set][i].dirty){
            uint8_t tmp[BURST_LEN << 1];
            memset(tmp, 1, sizeof(tmp));
            hwaddr_t addr_wb=(cache_L2[set][i].tag<<(cache_L2_b+cache_L2_s))|(set<<cache_L2_b);
            for(i0=0;i0<cache_L2_B/BURST_LEN;i0++){
                ddr3_write_me(addr_wb+8*i0, cache_L2[set][i].data+8*i0,tmp);
            }
            cache_L2[set][i].dirty=false;
        }
    }
    for(i0=0;i0<cache_L2_B/BURST_LEN;i0++){
        ddr3_read_me(addr_start+8*i0,cache_L2[set][i].data+8*i0);
    }
    cache_L2[set][i].valid=true;
    cache_L2[set][i].tag=tag;
    //ddr3_read_me(addr,cache_L2[set][i].data);不用循环的话感觉也可以
    return i;
}
void dram_write(hwaddr_t addr, size_t len, uint32_t data);//声明dram.c中dram_write函数

void write_cache_L1(hwaddr_t addr, size_t len, uint32_t data);
void write_cache_L2(hwaddr_t addr, size_t len, uint32_t data);
void write_cache_L1(hwaddr_t addr, size_t len, uint32_t data){
    uint32_t set=(addr>>cache_L1_b)&(cache_L1_S-1);
    uint32_t tag=addr>>(cache_L1_b+cache_L1_s);
    uint32_t bias=addr&(cache_L1_B-1);
    int i;
    for(i=0;i<cache_L1_E;i++){
        if(cache_L1[set][i].valid&&(cache_L1[set][i].tag==tag)){
            if(bias+len>cache_L1_B){
                dram_write(addr, cache_L1_B-bias, data);
                memcpy(cache_L1[set][i].data+bias, &data, cache_L1_B-bias);
                write_cache_L2(addr,cache_L2_B-bias, data);
                write_cache_L1(addr+cache_L1_B-bias, len-(cache_L1_B-bias), data>>(cache_L1_B-bias));
            }
            else{
                dram_write(addr, len, data);
                memcpy(cache_L1[set][i].data+bias, &data, len);
                write_cache_L2(addr, len, data);
            }
            return;
        }
    }
    //dram_write(addr, len, data);
    write_cache_L2(addr, len, data);
    return;
}
void write_cache_L2(hwaddr_t addr, size_t len, uint32_t data){
    uint32_t set=(addr>>cache_L2_b)&(cache_L2_S-1);
    uint32_t tag=addr>>(cache_L2_b+cache_L2_s);
    uint32_t bias=addr&(cache_L2_B-1);
    int i;
    for(i=0;i<cache_L2_E;i++){
        if(cache_L2[set][i].valid&&(cache_L2[set][i].tag==tag)){
            if(bias+len>cache_L2_B){
                memcpy(cache_L2[set][i].data+bias, &data, cache_L2_B-bias);
                write_cache_L2(addr+cache_L2_B-bias, len-(cache_L2_B-bias), data>>(cache_L2_B-bias));
            }
            else{
                memcpy(cache_L2[set][i].data+bias, &data, len);
            }
            cache_L2[set][i].dirty=true;
            return;
        }
    }
    i=read_cache_L2(addr);
    cache_L2[set][i].dirty=true;
    memcpy(cache_L2[set][i].data+bias,&data,len);
    return;
}